﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows.Forms;
using System.Xml;
using ClassLibrary.Agent;
using ClassLibrary.Configuration.Switch;

namespace ControlNode
{
    public partial class ControlNode : Form
    {
        internal static int CNodeId;
        private readonly NetworkCallController _ncc;
        private readonly ConnectionController _cc;
        private readonly Directory _dr;
        private PacketController _pc;
        private LinkResourceManager _lrm;
        private readonly RoutingController _rc;



        // --- port Zarządcy- odczytywany z plików konfiguracyjnych
        public int _port;
        // --- socket Zarządcy
        private Socket server;
        // --- pomocnicze przy otrzymywaniu wiadomości (z id) od agentów
        private byte[] data = new byte[1024];
        private int size = 1024;

        //[logicalPortId]
        private readonly Dictionary<String, Socket> _clientSocketsDictionary = new Dictionary<string, Socket>();



        public ControlNode()
        {
         

            _port = 9048;

            InitializeComponent();
            //NCC = new NetworkCallController();
            _lrm = new LinkResourceManager();
            _dr = new Directory(_clientSocketsDictionary);
            _rc = new RoutingController();
            _cc = new ConnectionController(_rc, _lrm, _dr);
            _ncc = new NetworkCallController(_cc);
            _pc = new PacketController(_ncc);
            PacketController.Log += WriteLine;
            Start();

            Logger.form = this;
            Logger.listBox1 = listBox1;
        }



        /// <summary>
        ///  Startuje serwer Managera
        /// 
        /// </summary>



        private void Start()
        {
            server = new Socket(AddressFamily.InterNetwork,
                    SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint iep = new IPEndPoint(IPAddress.Any, _port);
            server.Bind(iep);
            server.Listen(4);
            server.BeginAccept(AcceptConn, server);

        }


        /// <summary>
        /// Asynchronicza akceptacja podłączenia nowego agenta
        /// </summary>
        /// <param name="iar">Status operacji asynchronicznej</param>
        void AcceptConn(IAsyncResult iar)
        {
            Socket client = server.EndAccept(iar);


            client.BeginReceive(data, 0, size, SocketFlags.None,
                        OnIdReceived, client);


            server.BeginAccept(AcceptConn, server);
        }

        /// <summary>
        /// Asynchroniczne odebranie Id nowo podłączonego agenta
        /// </summary>
        /// <param name="asyn">Status operacji asynchronicznej</param>


        public void OnIdReceived(IAsyncResult asyn)
        {

            var mSocClient = (Socket)asyn.AsyncState;
            var iRx = 0;
            iRx = mSocClient.EndReceive(asyn);
            var chars = new char[iRx + 1];
            var d = System.Text.Encoding.UTF8.GetDecoder();
            d.GetChars(data, 0, iRx, chars, 0);
            var id = new String(chars).TrimEnd('\0');
            _clientSocketsDictionary[id] = mSocClient;
            mSocClient.BeginReceive(data, 0, data.Length, SocketFlags.None, ReceiveData, id);

        }

        /// <summary>
        /// Asynchronicze wysyłanie wiadomości konfiguracyjnej do agenta
        /// </summary>
        /// <param name="iar">Status operacji asynchronicznej</param>

        void SendData(IAsyncResult iar)
        {

            var id = (string)iar.AsyncState;
            var client = _clientSocketsDictionary[id];
            int sent = client.EndSend(iar);


        }

        /// <summary>
        /// Asynchroniczne odebranie wiadomości (tablicy routingowej) od agenta
        /// </summary>
        /// <param name="iar">Status operacji asynchronicznej</param>


        void ReceiveData(IAsyncResult iar)
        {
            try
            {
                var id = (String)iar.AsyncState;
                var mSocClient = _clientSocketsDictionary[id];
                try
                {
                    var iRx = mSocClient.EndReceive(iar);
                    var binaryFormatter = new BinaryFormatter();
                    var memoryStream = new MemoryStream(data, 0, iRx);
                    binaryFormatter.Deserialize(memoryStream);

                    mSocClient.BeginReceive(data, 0, data.Length, SocketFlags.None, ReceiveData, id);


                }

                catch (NullReferenceException ex)
                {

                }

                catch (SocketException ex)
                {


                    _clientSocketsDictionary.Remove(id);

                }
                catch (Exception ex)
                {
                }
            }
            catch (KeyNotFoundException ex)
            {

            }

        }
        public void WriteLine(string message)
        {
            this.Invoke((MethodInvoker)delegate
            {
                string Time = DateTime.Now.ToString("[HH:mm:ss tt] ");
                listBox1.Items.Add(Time + message);
            });
        }

        public static string CNodeID { get; set; }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Stream myStream = null;
            OpenFileDialog openFileDialog1 = new OpenFileDialog();

            openFileDialog1.InitialDirectory = "c:\\";
            openFileDialog1.Filter = "(*.xml)|*.xml";
            openFileDialog1.FilterIndex = 2;
            openFileDialog1.RestoreDirectory = true;

            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                try
                {

                    if ((myStream = openFileDialog1.OpenFile()) != null)
                    {
                        using (myStream)
                        {
                            LoadConfig(myStream);
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
                }
            }
        }


    

        private void sendSetSwitchingTableToNode(String nodeId, int ingressPortId, int ingressVcId, int engressVcId, int engressPortId)
        {

           
            var query = new Query()
            {
                QueryType = QueryType.Add,
                Record = new ConfSwitchingTable.Record()
                {
                    Ingress = { PortId = ingressPortId, VcId = ingressVcId },
                    Egress = { PortId = engressVcId, VcId = engressPortId }
                }
            };

            String choosen = nodeId;
 //comboBox1.SelectedItem.ToString();

                try
                {
                    var binaryFormatter = new BinaryFormatter();
                    var memoryStream = new MemoryStream();
                    binaryFormatter.Serialize(memoryStream, query);
                    var serializedData = memoryStream.ToArray();
                    _clientSocketsDictionary[choosen].BeginSend(serializedData, 0, serializedData.Length, SocketFlags.None,
                        new AsyncCallback(SendData), choosen);
                }
                catch (KeyNotFoundException ex)
                {
                    MessageBox.Show("Choosen Root is not connected", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }

            
        }

        /// <summary>
        /// Metoda odpoiwedzialna za załadowanie i przypisanie odpowiednim zmiennymn
        /// wartości zawartych w pliku konfiguracyjnym.
        /// </summary>
        /// <param name="myStream">Strumień z plikiem konfiguracyjnym</param>

        private void LoadConfig(Stream myStream)
        {
            XmlDocument doc = new XmlDocument();
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.ConformanceLevel = ConformanceLevel.Auto;
            settings.IgnoreWhitespace = true;
            settings.IgnoreComments = true;

            doc.Load(myStream);
            XmlNodeReader noder = new XmlNodeReader(doc);
            XmlReader reader = XmlReader.Create(noder, settings);
            while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.Element)
                {
                    if (reader.Name == "Port")
                    {
                        reader.Read();
                        _port = Int32.Parse(reader.Value.ToString());
                    }

                }
            }
        }


        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

    }
}
